package com.fancy.common.utils.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.fancy.common.results.Results;
import com.fancy.common.utils.http.HttpConnManager;
import com.fancy.common.utils.http.HttpUtil.HTTPMethod;

/**
 * HTTP工具类
 * 
 * @author xinfeng.hu 2013-3-19下午1:37:19
 * @version 1.0.0
 * @category 杭州广桥网络技术有限公司(商业先知)
 */
public final class HttpUtil {

	private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);

	public static String get(String uri) {
		return request(HTTPMethod.GET, uri, null, null);
	}

	public static String post(String uri, Map<String, ? extends Serializable> params) {
		return request(HTTPMethod.POST, uri, params, null);
	}
	
	public static String postJson(String uri, String json) {
		String result="";
		HttpClient client = uri.startsWith("https://") ? HttpConnManager.getHttpsClient() : HttpConnManager.getHttpClient();
		HttpPost post = new HttpPost(uri);
		try {
			StringEntity s = new StringEntity(json, Charset.forName("UTF-8"));
			s.setContentEncoding("UTF-8");
			s.setContentType("application/json");
			post.setEntity(s);

			HttpResponse response = client.execute(post);
			result = EntityUtils.toString(response.getEntity(), Charset.forName("gbk"));

		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return result;
	}
	
	public static String request(String cookie, HTTPMethod httpMethod, String uri, Map<String, ? extends Serializable> params) {
		Map<String, String> headers = new HashMap<String, String>();
		headers.put("cookie", cookie);
		return request(httpMethod, uri, params, headers);
	}

	public static String request(HTTPMethod httpMethod, String uri, Map<String, ? extends Serializable> params) throws IOException {
		return request(httpMethod, uri, params, null);
	}

	public static String request(HTTPMethod httpMethod, String uri, Map<String, ? extends Serializable> params, Map<String, String> headers) {
		HttpUriRequest request = createHttpUriRequest(httpMethod, uri, params);
		if (headers != null && headers.size() > 0) {
			for (Map.Entry<String, String> entry : headers.entrySet()) {
				request.addHeader(entry.getKey(), entry.getValue());
			}
		}
		HttpClient client = uri.startsWith("https://") ? HttpConnManager.getHttpsClient() : HttpConnManager.getHttpClient();
		try {
			return client.execute(request, responseStringHandler);
		} catch (ClientProtocolException e) {
			logger.error("request error uri=" + uri, e);
		} catch (IOException e) {
			logger.error("request error uri=" + uri, e);
		} finally {
			abortConn(request);
		}
		return null;
	}

	public static InputStream requestStream(HTTPMethod httpMethod, String uri, Map<String, ? extends Serializable> params, Map<String, String> headers) {
		return requestStream(httpMethod, uri, params, headers, false);
	}
	
	
	public static InputStream requestStream(HTTPMethod httpMethod, String uri, Map<String, ? extends Serializable> params,
			Map<String, String> headers, boolean byBrowser) {
		HttpUriRequest request = createHttpUriRequest(httpMethod, uri, params);
		if (headers != null && headers.size() > 0) {
			for (Map.Entry<String, String> entry : headers.entrySet()) {
				request.addHeader(entry.getKey(), entry.getValue());
			}
		}
		HttpClient client = uri.startsWith("https://") ? HttpConnManager.getHttpsClient(byBrowser) : HttpConnManager.getHttpClient(byBrowser);
		try {
			return client.execute(request, responseStreamHandler);
		} catch (ClientProtocolException e) {
			logger.error("request error uri=" + uri, e);
		} catch (IOException e) {
			logger.error("request error uri=" + uri, e);
		} finally {
			abortConn(request);
		}
		return null;
	}

	protected static HttpUriRequest createHttpUriRequest(HTTPMethod httpMethod, String uri, Map<String, ? extends Serializable> params) {
		switch (httpMethod) {
		case GET:
			return new HttpGet(setParam(uri, params));
		case DELETE:
			return new HttpDelete(uri);
		case HEAD:
			return new HttpHead(uri);
		case OPTIONS:
			return new HttpOptions(uri);
		case POST:
			return setParam(new HttpPost(uri), params);
		case PUT:
			return new HttpPut(uri);
		case TRACE:
			return new HttpTrace(uri);
		default:
			throw new IllegalArgumentException("Invalid HTTP method: " + httpMethod);
		}
	}
	public static Results<String> requestStream(final HTTPMethod httpMethod, final String uri, final Function<InputStream, Results<String>> callback,
			final boolean byBrowser) {
		return requestStream(httpMethod, uri, callback, null, null, byBrowser);
	}

	public static Results<String> requestStream(final HTTPMethod httpMethod, final String uri, final Function<InputStream, Results<String>> callback,
			final Map<String, ? extends Serializable> params, final Map<String, String> headers) {
		return requestStream(httpMethod, uri, callback, params, headers, false);
	}
	public static Results<String> requestStream(final HTTPMethod httpMethod, final String uri, final Function<InputStream, Results<String>> callback,
			final Map<String, ? extends Serializable> params, final Map<String, String> headers, final boolean byBrowser) {
		HttpUriRequest request = createHttpUriRequest(httpMethod, uri, params);
		if (headers != null && headers.size() > 0) {
			for (Map.Entry<String, String> entry : headers.entrySet()) {
				request.addHeader(entry.getKey(), entry.getValue());
			}
		}

		HttpClient client = uri.startsWith("https://") ? HttpConnManager.getHttpsClient(byBrowser) : HttpConnManager.getHttpClient(byBrowser);
		try {
			return client.execute(request, new ResponseHandler<Results<String>>() {
				// 自定义响应处理
				@Override
				public Results<String> handleResponse(HttpResponse response) throws IOException {
					HttpEntity entity = response.getEntity();
					if (entity != null) {
						return callback.apply(entity.getContent());
					} else {
						return null;
					}
				}
			});
		} catch (ClientProtocolException e) {
			logger.error("request error uri=" + uri, e);
		} catch (IOException e) {
			logger.error("request error uri=" + uri, e);
		} finally {
			abortConn(request);
		}
		return null;
	}
	public static void destroy() {
		HttpConnManager.getCm().shutdown();
	}

	// 使用ResponseHandler接口处理响应，HttpClient使用ResponseHandler会自动管理连接的释放，解决了对连接的释放管理
	private static ResponseHandler<String> responseStringHandler = new ResponseHandler<String>() {
		// 自定义响应处理
		public String handleResponse(HttpResponse response) throws IOException {
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				return EntityUtils.toString(entity, Charset.forName("utf-8"));
			} else {
				return null;
			}
		}
	};

	// 使用ResponseHandler接口处理响应，HttpClient使用ResponseHandler会自动管理连接的释放，解决了对连接的释放管理
	private static ResponseHandler<InputStream> responseStreamHandler = new ResponseHandler<InputStream>() {
		// 自定义响应处理
		public InputStream handleResponse(HttpResponse response) throws IOException {
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				return entity.getContent();
			} else {
				return null;
			}
		}
	};

	/**
	 * Get方式提交,URL中不包含查询参数, 格式：http://www.g.cn
	 * 
	 * @param url
	 *            提交地址
	 * @param params
	 *            查询参数集, 键/值对
	 * @return 响应消息
	 */
	public static String setParam(String url, Map<String, ? extends Serializable> params) {
		if (params != null && params.size() > 0) {
			List<NameValuePair> qparams = getParamsList(params);
			if (qparams != null && qparams.size() > 0) {
				String formatParams = URLEncodedUtils.format(qparams, Charset.forName("utf-8"));
				url = url + (url.indexOf("?") < 0 ? "?" : "&") + formatParams;
			}
		}

		return url;
	}

	public static HttpEntityEnclosingRequestBase setParam(HttpEntityEnclosingRequestBase request, Map<String, ? extends Serializable> params) {
		if (params != null && params.size() > 0) {
			UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(getParamsList(params), Charset.forName("UTF-8"));
			request.setEntity(formEntity);
		}
		return request;
	}

	/**
	 * 将传入的键/值对参数转换为NameValuePair参数集
	 * 
	 * @param paramsMap
	 *            参数集, 键/值对
	 * @return NameValuePair参数集
	 */
	private static List<NameValuePair> getParamsList(Map<String, ? extends Serializable> paramsMap) {
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		for (Map.Entry<String, ? extends Serializable> map : paramsMap.entrySet()) {
			params.add(new BasicNameValuePair(map.getKey(), map.getValue().toString()));
		}
		return params;
	}

	/**
	 * 释放HttpClient连接
	 * 
	 * @param hrb
	 *            请求对象
	 */
	private static void abortConn(final HttpUriRequest hrb) {
		if (hrb != null) {
			hrb.abort();
		}
	}

	public enum HTTPMethod {
		GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE
	}
}

